1. 简介 先看看英文介绍
可以看出SnapHelper是对RecyclerView的一种拓展功能,支持snapping。SnapHelper通过处理RecyclerView的fling,来达到要展示的效果,但是这需要一个前提条件,就是RecyclerView的LayoutManager必须实现ScrollVectorProvider接口或者手动实现onFling接口,自己实现fling处理。
SnapHelper的实现原理是监听RecyclerView.OnFlingListener中的onFling接口。LinearSnapHelper&PagerSnapHelper是抽象类SnapHelper的具体实现。 区别在于: LinerSnapHelper,可滑动多页,居中显示; PagerSnapHelper,每次只能滑动一页,居中显示;
2. 基本实现 2.1 横向LinearSnapHelper 1 2 3 4 5 6 7 LinearLayoutManager llm = new LinearLayoutManager(this , LinearLayoutManager.HORIZONTAL, false ); CardAdapter cardAdapter = new CardAdapter(this ); LinearSnapHelper linearSnapHelper = new LinearSnapHelper(); mRvContent.setLayoutManager(llm); mRvContent.setAdapter(cardAdapter); linearSnapHelper.attachToRecyclerView(mRvContent);
2.2 纵向LinearSnapHelper 1 2 3 4 5 6 7 LinearLayoutManager llm = new LinearLayoutManager(this , LinearLayoutManager.VERTICAL, false ); CardAdapter cardAdapter = new CardAdapter(this ); LinearSnapHelper linearSnapHelper = new LinearSnapHelper(); mRvContent.setLayoutManager(llm); mRvContent.setAdapter(cardAdapter); linearSnapHelper.attachToRecyclerView(mRvContent);
1 2 3 4 5 6 7 LinearLayoutManager llm = new LinearLayoutManager(this , LinearLayoutManager.HORIZONTAL, false ); CardAdapter cardAdapter = new CardAdapter(this ); PagerSnapHelper pagerSnapHelper = new PagerSnapHelper(); mRvContent.setLayoutManager(llm); mRvContent.setAdapter(cardAdapter); pagerSnapHelper.attachToRecyclerView(mRvContent);
一般步骤:
创建SnapHelper;
attachToRecyclerView。
3. 源码分析 SnapHelper源码结构:
SnapHelper是一个抽象类,无构造方法。其中有三个抽象方法是需要子类去实现的
方法名
描述
calculateDistanceToFinalSnap
计算第二个参数对应的ItemView当前的坐标与需要对齐的坐标之间的距离。返回一个int[2]数组,分别对应x轴和y轴方向上的距离
findSnapView
找到最接近对齐位置的view,该view称为SanpView,对应的position称为SnapPosition。如果返回null,就表示没有需要对齐的View,也就不会做滚动对齐调整
findTargetSnapPosition
根据Fling操作的速率(参数velocityX和参数velocityY)找到需要滚动到的targetSnapPosition,该位置对应的View就是targetSnapView。如果找不到targetSnapPosition,就返回RecyclerView.NO_POSITION
3.1 attachToRecyclerView 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public void attachToRecyclerView (@Nullable RecyclerView recyclerView) throws IllegalStateException { if (mRecyclerView == recyclerView) { return ; } if (mRecyclerView != null ) { destroyCallbacks(); } mRecyclerView = recyclerView; if (mRecyclerView != null ) { setupCallbacks(); mGravityScroller = new Scroller(mRecyclerView.getContext(), new DecelerateInterpolator()); snapToTargetExistingView(); } }
1.判断绑定的RecyclerView 和之前的是否相同,不重复绑定; 2.如果传入的RecyclerView不为空,则执行destoryCallbacks(), 移除滚动监听和onFlingListener
1 2 3 4 private void destroyCallbacks () { mRecyclerView.removeOnScrollListener(mScrollListener); mRecyclerView.setOnFlingListener(null ); }
3.赋值成员变量mRecyclerView 4.setupCallbacks()和destoryCallbacks()对应
1 2 3 4 5 6 7 8 private void setupCallbacks () throws IllegalStateException { if (mRecyclerView.getOnFlingListener() != null ) { throw new IllegalStateException("An instance of OnFlingListener already set." ); } mRecyclerView.addOnScrollListener(mScrollListener); mRecyclerView.setOnFlingListener(this ); }
5.创建mGravityScroller 6.snapToTargetExistingView() 这个方法在这里调用一次,其实更关键的调用地方是在OnScrollListener中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private final RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() { boolean mScrolled = false ; @Override public void onScrollStateChanged (RecyclerView recyclerView, int newState) { super .onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_IDLE && mScrolled) { mScrolled = false ; snapToTargetExistingView(); } } @Override public void onScrolled (RecyclerView recyclerView, int dx, int dy) { if (dx != 0 || dy != 0 ) { mScrolled = true ; } } };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void snapToTargetExistingView () { if (mRecyclerView == null ) { return ; } LayoutManager layoutManager = mRecyclerView.getLayoutManager(); if (layoutManager == null ) { return ; } View snapView = findSnapView(layoutManager); if (snapView == null ) { return ; } int [] snapDistance = calculateDistanceToFinalSnap(layoutManager, snapView); if (snapDistance[0 ] != 0 || snapDistance[1 ] != 0 ) { mRecyclerView.smoothScrollBy(snapDistance[0 ], snapDistance[1 ]); } }
3.2 onFling Fling操作: 手指在屏幕上滑动RecyclerView后松手,RecyclerView中的内容会靠着惯性继续往之前滑动的方向继续滚动直到停止,这个过程叫做Fling。Fling操作从手指离开屏幕瞬间被触发,在滚动停止时结束。
刚才在setupCallbacks()中设置的onFlingerListener
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public boolean onFling (int velocityX, int velocityY) { LayoutManager layoutManager = mRecyclerView.getLayoutManager(); if (layoutManager == null ) { return false ; } RecyclerView.Adapter adapter = mRecyclerView.getAdapter(); if (adapter == null ) { return false ; } int minFlingVelocity = mRecyclerView.getMinFlingVelocity(); return (Math.abs(velocityY) > minFlingVelocity || Math.abs(velocityX) > minFlingVelocity) && snapFromFling(layoutManager, velocityX, velocityY); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private boolean snapFromFling (@NonNull LayoutManager layoutManager, int velocityX, int velocityY) { if (!(layoutManager instanceof ScrollVectorProvider)) { return false ; } RecyclerView.SmoothScroller smoothScroller = createSnapScroller(layoutManager); if (smoothScroller == null ) { return false ; } int targetPosition = findTargetSnapPosition(layoutManager, velocityX, velocityY); if (targetPosition == RecyclerView.NO_POSITION) { return false ; } smoothScroller.setTargetPosition(targetPosition); layoutManager.startSmoothScroll(smoothScroller); return true ; }
有兴趣的盆友可以去查阅下LinearSnapHelper和PagerSnapHelper的源码。 推荐源码阅读网站androidxref.com
4. 源码 RecyclerViewGallery
5. 推荐开源库 RecyclerViewSnap